package com.wn.sk.product.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wn.sk.Asserts;
import com.wn.sk.feign.dto.OrderDto;
import com.wn.sk.order.client.OrderFeignClient;
import com.wn.sk.product.contants.PromotionStatus;
import com.wn.sk.product.contants.RedisKey;
import com.wn.sk.product.entity.Product;
import com.wn.sk.product.entity.PromotionSeckill;
import com.wn.sk.product.entity.vo.PromotionOrderVo;
import com.wn.sk.product.entity.vo.PromotionProductVo;
import com.wn.sk.product.mapper.ProductMapper;
import com.wn.sk.product.service.IProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wn.sk.product.service.IPromotionSeckillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 商品表 服务实现类
 * </p>
 *
 * @author yellow docker
 * @since 2022-11-07
 */
@Service
@Slf4j
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private IPromotionSeckillService promotionSeckillService;

    // 注册操作redis的模板
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    /**
     *  RedisTemplate 的内部使用的是jdk序列化,内部操作 使用的是 objectOutputStream io流，
     *  对象从存储是二进制数据
     *  解决:
     *     1、自己配置 序列化对象 替换 内置的jdk的序列化 操作
     *
     */
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderFeignClient orderFeignClient;

    @Autowired
    private IProductService productService;


    /**
     *   查询 描述商品列表 ,不要放到MySQL中 存储到redis中
     *
     *   1、使用RedisTemplate 存储到 redis中
     *      1、页面访问过来
     *      2、查询redis 如果redis中没有 查询数据库
     *      3、第一次查询的结果存储到redis中，后期的查询从redis中获取
     *
     *  2、使用注解     存储到 redis中
     *    1、开启注解 使用缓存
     *    2、配置注解
     *
     *    @Cacheable(value = "product:sk" ,key = "'list'")
     *    设置缓存
     *      1、@Cacheable 第一次查询数据,后面会从redis获取
     *      2、存储在redis中的key = value + key (SPel表达式)如果写字符串需要用'字符串'
     *
     * @return
     */
    @Cacheable(value = "product:sk" ,key = "'list'")
    @Override
    public List<PromotionProductVo> findPromotionProduct(int status) throws JsonProcessingException {
        List<PromotionProductVo> vos = productMapper.findPromotionProduct(status);
        return vos;
    }

    /**
     * 秒杀活动商品的订单产生
     * @param promotionId
     * @param userId
     * @return
     */
    @Override
    public PromotionOrderVo submitOrder(Integer promotionId, String userId) {

        //秒杀活动对象
        PromotionSeckill promotionSeckill = promotionSeckillService.getById(promotionId);
        Integer status = promotionSeckill.getStatus();
        //01 判断这个商品是否参与秒杀
        if (status == PromotionStatus.UN_START){
            Asserts.failed("秒杀商品未开始活动....");
        }
        //02 判断当前的用户是否已经秒杀过该商品  redis中操作 set的api
        // isMember 判断 set中是否 有这个商品
        //sk:user:1           1
        //sk:user:1           1,2      isMember判断这个set中是否有 商品编号1、2、xxx

        String key =RedisKey.PRODUCT_SK_USER + ":" + userId;
        Boolean isMember = redisTemplate.opsForSet().isMember(key, promotionId);
        if (isMember){
            Asserts.failed("亲你已秒杀过该商品 不能重复的秒杀 看看其它的商品....");
        }
        //03 创建一个订单
        log.info("创建了一个订单");

        OrderDto orderDto = new OrderDto();
        Product product = productService.getById(promotionSeckill.getProductId());
        orderDto.setUserId(userId);
        orderDto.setProductId(promotionSeckill.getProductId());
        orderDto.setProductName(product.getTitle());
        //商品价格
        orderDto.setPrice(product.getPrice());
        //秒杀活动价
        orderDto.setPromotionPrice(product.getPromotionPrice());

        //远程调用 返回一个 orderSn
        String orderSn = orderFeignClient.createOrder(orderDto).getData();

        //04 减库存
        String stockKey = RedisKey.PRODUCT_SK_STOCK + ":" + promotionId;
        redisTemplate.opsForList().leftPop(stockKey);

        //记录用户已经买了这个商品
        redisTemplate.opsForSet().add(key,promotionId);

        //05 返回结果（秒杀活动的订单的vo）
        PromotionOrderVo promotionOrderVo = PromotionOrderVo.builder().userId(userId).orderSn(orderSn).build();

        return promotionOrderVo;
    }

    public List<PromotionProductVo> redisCache() throws JsonProcessingException {

        ObjectMapper objectMapper = new ObjectMapper();

        /**
         * 01 从redis中查询
         *
         * 02 如果有数据返回
         *
         * 03 没有数据 查询mysql 存储到redis中 (存储到redis中需要把对象转成json字符串)
         *
         * redisKey=模块:业务:id
         *
         */
        String redisJson = stringRedisTemplate.opsForValue().get(RedisKey.PRODUCT_SK_LIST);

        if (StringUtils.hasText(redisJson)){
            log.info("从redis缓存中获取数据");

            // 集合的类型   ArrayList
            // 集合类型中的泛型 PromotionProductVo
            JavaType javaType = objectMapper
                    .getTypeFactory()
                    .constructParametricType(ArrayList.class, PromotionProductVo.class);

            List<PromotionProductVo> productVos =  objectMapper.readValue(redisJson,javaType);

            return productVos;
        }
        //查询 mysql
        List<PromotionProductVo> vos = productMapper.findPromotionProduct(0);

        //存储到redis中   obj - > json  字符串
        if (!CollectionUtils.isEmpty(vos)){
            log.info("第一次查询把数据存储到redis中 ");
            //把查询的存储到redis中
            String str = objectMapper.writeValueAsString(vos);
            stringRedisTemplate.opsForValue().set(RedisKey.PRODUCT_SK_LIST,str);
        }
        return  vos;
    }
}
